--------------------------------------------------------------------------------
-- 
-- S3PICPROG - PIC programmer with S3 starter kit
-- Copyright (C) 2008 Olivier Ringot <oringot@gmail.com>
-- 
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 2
-- of the License, or (at your option) any later version.
-- 
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
-- 
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, 
-- Boston, MA  02110-1301, USA.
-- 
--------------------------------------------------------------------------------
-- 
-- $Revision: $
-- $Date: $
-- $Source: $
-- $Log: $
--
--------------------------------------------------------------------------------
-- PROTOCOL  ack: 0=success/1=error/2=unknown command / XXXXXX is register addr
-- -----------------------------------------------------------------------------
-- core_instr              <0x10> <data0> <data1>          <ack>
--
-- read                    <0x20>                          <data0> <ack>
-- read32                  <0x21>                          <data0>...<data31> <ack>
--
-- write                   <0x30> <data0> <data1>          <ack>
-- write_prog              <0x31> <data0> <data1>          <ack>
-- write32_prog            <0x32> <data0>...<data31>       <ack>
--
-- delay hperiod           <0x80> <data0> <data1>          <ack>
-- delay prog              <0x81> <data0> <data1>          <ack>                  
-- delay erase             <0x82> <data0> <data1>          <ack>                  
-- led                     <0x83> <led>   <dummy>          <ack>
--
-- pic pins state          <0x90> <data0> <data1>          <ack>
--        [15]: driver (o=off/1=on)  [1]=pgm   [0]=mclr
--
-- <ack>: 0=success/1=error
--
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library s3picprog_lib;
use s3picprog_lib.s3picprog_pkg.all;

entity s3picprog is
  port
  (
    -- system
    osc50          :    in std_logic;
    
    -- switch
    pbutton        :    in std_logic_vector( 3 downto 0);
    switch         :    in std_logic_vector( 7 downto 0);
    
    -- 4x7 segments display
    led            :   out std_logic_vector( 7 downto 0);
    
    -- rs-232
    uart_t2h       :   out std_logic;  -- FPGA to PC
    uart_h2t       :    in std_logic;  -- PC to FPGA
    uart_p2t       :    in std_logic;  -- PIC to FPGA     
    uart_t2p       :   out std_logic;  -- FPGA to PIC
    
    -- PIC interface
    pic_mclr       : inout std_logic;
    pic_pgm        : inout std_logic;
    pic_pgc        : inout std_logic;
    pic_pgd        : inout std_logic;
    
    -- unused
    anode_s7       :   out std_logic_vector( 3 downto 0);
    s7             :   out std_logic_vector( 7 downto 0);
    --
    sram_csn       :   out std_logic_vector( 1 downto 0);
    sram_ben       :   out std_logic_vector( 3 downto 0);
    sram_oen       :   out std_logic;
    sram_wen       :   out std_logic;
    sram_a         :   out std_logic_vector(17 downto 0);
    sram_dq        : inout std_logic_vector(31 downto 0)
  );
end entity s3picprog;

architecture rtl of s3picprog is

  signal async_nrst         : std_logic;
  signal root50m_nrst_1t    : std_logic;
  signal root50m_nrst_2t    : std_logic;
  signal root50m_nrst_3t    : std_logic;
  signal root50m_nrst       : std_logic;
  
  signal enable1m           : std_logic;
  signal counter1m          : std_logic_vector(5 downto 0);
  signal uart_clken         : std_logic;
  signal uart_counter       : std_logic_vector(6 downto 0);
  
  type t_state         is (s_idle,s_receive_data0,
                                s_receive_data1,s_execute,
                                s_wait_icsp_done,
                                s_send_data,s_wait_sending_data,
                                s_send_ack,s_wait_sending_ack);                              
  signal state         : t_state;
  
  signal uart_wen           : std_logic;
  signal uart_busy          : std_logic;
  signal uart_busy_1t       : std_logic;
  signal uart_valid         : std_logic;
  signal uart_wdata         : std_logic_vector( 7 downto 0);
  signal uart_rdata         : std_logic_vector( 7 downto 0);
  signal uart_tx            : std_logic;
  signal uart_rx            : std_logic;
 
  signal icsp_start         : std_logic;                         
  signal icsp_done          : std_logic;                         
  signal icsp_command       : std_logic_vector( 3 downto 0);     
  signal icsp_datai         : std_logic_vector(15 downto 0);     
  signal icsp_datao         : std_logic_vector( 7 downto 0);     
  signal icsp_delay         : std_logic_vector(18 downto 0);     
  signal icsp_sr_counter    : std_logic_vector( 5 downto 0);     
  signal icsp_sri           : std_logic_vector(19 downto 0);     
  signal icsp_sro           : std_logic_vector(19 downto 0);     
  signal icsp_pgc           : std_logic;                         
  signal icsp_pgdo          : std_logic;                         
  signal icsp_pgdi          : std_logic;                         
  signal icsp_pgde          : std_logic;                         
  
  signal burst_count        : std_logic_vector( 4 downto 0);
  signal reg_addr           : std_logic_vector( 7 downto 0);
  signal reg_data           : std_logic_vector(15 downto 0);
  signal reg_led            : std_logic_vector( 7 downto 0);                         
  signal reg_icsp_oe        : std_logic;                         
  signal reg_icsp_mclr      : std_logic;                         
  signal reg_icsp_pgm       : std_logic;
  signal reg_delay_hperiod  : std_logic_vector(15 downto 0);                         
  signal reg_delay_prog     : std_logic_vector(15 downto 0);                         
  signal reg_delay_erase    : std_logic_vector(15 downto 0);                         

  signal switch_1t          : std_logic;
  signal switch_2t          : std_logic;

begin

  -- ---------------------------------------------------------------------------
  -- assignments
  -- ---------------------------------------------------------------------------
  -- unused
  anode_s7 <= (others=>'1');
  s7       <= (others=>'1');
  --
  sram_csn <= (others=>'1');
  sram_ben <= (others=>'1');
  sram_wen <= '1';
  sram_oen <= '1';
  sram_a   <= (others=>'0');
  sram_dq  <= (others=>'Z');
  
  -- reset
  async_nrst <= not(pbutton(0));
  -- PIC ports
  pic_mclr    <= 'Z' when reg_icsp_oe='0' else reg_icsp_mclr;
  pic_pgm     <= 'Z' when reg_icsp_oe='0' else reg_icsp_pgm;
  pic_pgc     <= 'Z' when reg_icsp_oe='0' else icsp_pgc;
  pic_pgd     <= 'Z' when reg_icsp_oe='0' or (reg_icsp_oe='1' and icsp_pgde='0') else 
                 icsp_pgdo;
  icsp_pgdi   <= pic_pgd;
  
  -- led
  led <= reg_led;
  
  -- ---------------------------------------------------------------------------
  -- reset cleanup
  -- ---------------------------------------------------------------------------
  p_reset50m_cleaner:process(async_nrst,osc50)
  begin
  
    if async_nrst='0' then
    
      root50m_nrst_1t <= '0';
      root50m_nrst_2t <= '0';
      root50m_nrst_2t <= '0';
    
    elsif osc50'event and osc50='1' then
    
      root50m_nrst_1t <= '1';
      root50m_nrst_2t <= root50m_nrst_1t;
      root50m_nrst_3t <= root50m_nrst_2t;
    
    end if;
  
  end process p_reset50m_cleaner;
  
  root50m_nrst <= root50m_nrst_3t and async_nrst;
 
  -- ---------------------------------------------------------------------------
  -- 1 MHz clock generation
  -- ---------------------------------------------------------------------------
  p_enable1m_generation:process(root50m_nrst,osc50)
  begin
  
    if root50m_nrst='0' then
    
      enable1m     <= '0';
      counter1m    <= (others=>'0');
    
    elsif osc50='1' and osc50'event then
    
      if counter1m="110001" then
      
        enable1m   <= '1';
        counter1m  <= (others=>'0');
        
      else
      
        enable1m   <= '0';
        counter1m  <= counter1m + 1;
        
      end if;
    
    end if;
  
  end process p_enable1m_generation;
  
  -- ---------------------------------------------------------------------------
  -- UART clock enable 2.170138us
  -- ---------------------------------------------------------------------------
  p_uart_clken_generation:process(root50m_nrst,osc50)
  begin
  
    if root50m_nrst='0' then
    
      uart_clken   <= '0';
      uart_counter <= (others=>'0');
    
    elsif osc50'event and osc50='1' then
    
      uart_clken <= '0';
      
      if uart_counter="1101100" then -- 108
      
        uart_counter <= (others=>'0');
        uart_clken   <= '1';
        
      else
      
        uart_counter <= uart_counter + 1;
     
      end if;
    
    end if;
  
  end process p_uart_clken_generation;
  
  -- ---------------------------------------------------------------------------
  -- PC RS232 FPGA/PIC sharing
  -- ---------------------------------------------------------------------------
  p_switch_resync:process(root50m_nrst,osc50)
  begin
  
    if root50m_nrst='0' then
    
      switch_1t <= '0';
      switch_2t <= '0';
    
    elsif osc50'event and osc50='1' then
  
      switch_1t <= switch(0);
      switch_2t <= switch_1t;
  
    end if;
    
  end process p_switch_resync;
  
  p_uart_sharing:process(switch_2t,uart_tx,uart_h2t,uart_p2t)
  begin
  
    if switch_2t='0' then
    
     -- FPGA uart
     uart_t2h <= uart_tx;
     uart_rx  <= uart_h2t;
     
     -- PIC uart
     uart_t2p <= '1';
    
    else
    
     -- FPGA uart
     uart_rx <= '1';
     
     -- PIC uart
     uart_t2p <= uart_h2t;
     uart_t2h <= uart_p2t;
     
    end if;
  
  end process p_uart_sharing;
  
  -- ---------------------------------------------------------------------------
  -- FSM
  -- ---------------------------------------------------------------------------
  p_fsm:process(root50m_nrst,osc50)
  begin
  
    if root50m_nrst='0' then
    
      -- reg
      reg_addr           <= (others=>'0');
      reg_data           <= (others=>'0');
      reg_led            <= (others=>'0');
      reg_icsp_oe        <= '0';
      reg_icsp_pgm       <= '0';
      reg_icsp_mclr      <= '0';
      reg_delay_hperiod  <= (others=>'0');
      reg_delay_prog     <= (others=>'0');
      reg_delay_erase    <= (others=>'0');
      --
      icsp_command       <= (others=>'0');
      icsp_datai         <= (others=>'0');
      icsp_start         <= '0';
      --
      uart_wen           <= '0';
      uart_wdata         <= (others=>'0');
      uart_busy_1t       <= '0';
      -- state
      burst_count        <= (others=>'0');
      state         <= s_idle;
      
    elsif osc50'event and osc50='1' then
    
      -- uart flags
      uart_busy_1t <= uart_busy;
      
      -- FSM
      uart_wdata    <= (others=>'0');
      uart_wen      <= '0';
      
      -- icsp
      icsp_start    <= '0';
      
      case state is
      
       -- ----------------------------------------------------------------------
       -- IDLE
       -- ----------------------------------------------------------------------
       when s_idle=>
       
         if uart_valid='1' then
         
           reg_addr <= uart_rdata;
           
           case uart_rdata is
           
             -- CORE INSTR
             -- WRITE
             -- WRITE_PROG
             -- DELAY HPERIOD,PROG,ERASE
             -- LED
             -- PIC PIN STATE
             when "00010000"|
                  "00110000"|
                  "00110001"|
                  "10000000"|
                  "10000001"|
                  "10000010"|
                  "10000011"|
                  "10010000"=>
             
               state   <= s_receive_data0;
               
             -- WRITE32_PROG
             when "00110010"=>
            
               burst_count  <= "01111";
               state   <= s_receive_data0;
            
             -- READ
             -- READ32
             when "00100000"|
                  "00100001"=>
             
               state   <= s_execute;
             
             when others=>
             
               null;
           
           end case;
         
         end if;
   
       -- ----------------------------------------------------------------------
       -- RECEIVE_DATA
       -- ----------------------------------------------------------------------
       when s_receive_data0=>
       
        if uart_valid='1' then
        
          reg_data( 7 downto 0) <= uart_rdata;
          state            <= s_receive_data1;
          
        end if;
      
       when s_receive_data1=>
       
        if uart_valid='1' then
        
          reg_data(15 downto 8) <= uart_rdata;
          state             <= s_execute;
        
        end if;
    
       -- ----------------------------------------------------------------------
       -- EXECUTE
       -- ----------------------------------------------------------------------
       when s_execute=>
       
         case reg_addr is
         
           -- CORE_INSTR
           when "00010000"=>
           
             icsp_command <= "0000";
             icsp_datai   <= reg_data;
             icsp_start   <= '1';
             state   <= s_wait_icsp_done;
          
           -- WRITE
           when "00110000"=>
           
             icsp_command <= "1100";
             icsp_datai   <= reg_data;
             icsp_start   <= '1';
             state   <= s_wait_icsp_done;

           -- WRITE_PROG
           when "00110001"=>
           
             icsp_command <= "1111";
             icsp_datai   <= reg_data;
             icsp_start   <= '1';
             state   <= s_wait_icsp_done;

           -- WRITE32_PROG
           when "00110010"=>
           
             if burst_count="00000" then
             
               icsp_command <= "1111"; -- write and prog on the 32nd data
             
             else
             
               icsp_command <= "1101"; -- write and postinc2 on others data
             
             end if;
             
             icsp_datai   <= reg_data;
             icsp_start   <= '1';
             state   <= s_wait_icsp_done;

           -- DELAY_HPERIOD
           when "10000000"=>
           
             reg_delay_hperiod <= reg_data;
             reg_data          <= (others=>'0');
             state        <= s_send_ack;

           -- DELAY_PROG
           when "10000001"=>
           
             reg_delay_prog    <= reg_data;
             reg_data          <= (others=>'0');
             state        <= s_send_ack;
         
           -- DELAY_ERASE
           when "10000010"=>
           
             reg_delay_erase   <= reg_data;
             reg_data          <= (others=>'0');
             state        <= s_send_ack;
         
           -- LED
           when "10000011"=>
           
             reg_led      <= reg_data(7 downto 0);
             reg_data     <= (others=>'0');
             state   <= s_send_ack;
            
           -- PIC PIN STATE
           when "10010000"=>
           
             reg_icsp_oe   <= reg_data(15);
             reg_icsp_pgm  <= reg_data(1);
             reg_icsp_mclr <= reg_data(0);
             reg_data      <= (others=>'0');
             state         <= s_send_ack; 
         
           -- READ
           when "00100000"=>
           
             icsp_command <= "1000";
             icsp_start   <= '1';
             state   <= s_wait_icsp_done;

           -- READ_32
           when "00100001"=>
           
             icsp_command <= "1001";
             icsp_start   <= '1';
             burst_count  <= "11111";
             state   <= s_wait_icsp_done;
           
           -- ILLEGAL
           when others=>
           
             reg_data   <= "0000000000000001";
             state <= s_send_ack; 
        
         end case;
         
       -- ----------------------------------------------------------------------
       -- WAIT_ICSP_DONE
       -- ----------------------------------------------------------------------
       when s_wait_icsp_done=>
       
         if icsp_done='1' then
         
            case reg_addr is
            
              when "00100000"|  -- READ
                   "00100001"=> -- READ32
              
                state <= s_send_data;
              
              when "00010000"=> -- CORE INSTR
              
                reg_data   <= (others=>'0');
                state <= s_send_ack;
            
              when "00110000"|  -- WRITE
                   "00110001"=> -- WRITE_PROG
              
                reg_data   <= (others=>'0');
                state <= s_send_ack;
              
              when "00110010"=> -- WRITE32_PROG
              
                if burst_count="00000" then
                
                  reg_data   <= (others=>'0');
                  state <= s_send_ack;
                  
                else
                
                  burst_count <= burst_count - 1;
                  state  <= s_receive_data0;
                
                end if;
              
              when others=>
              
                reg_data   <= "0000000000000001";
                state <= s_send_ack;
                
            end case;
                
         end if;
  
       -- ----------------------------------------------------------------------
       -- SEND_DATA
       -- ----------------------------------------------------------------------
       when s_send_data=>
       
         uart_wdata <= icsp_datao;
         uart_wen   <= '1';
         state <= s_wait_sending_data;
         
       when s_wait_sending_data=>
       
         if uart_busy_1t='1' and uart_busy='0' then
         
           if reg_addr="00100000" then -- READ
           
             reg_data   <= (others=>'0');
             state <= s_send_ack;
             
           else -- READ32
           
             if burst_count="00000" then
             
               reg_data    <= (others=>'0');
               state  <= s_send_ack;
               
             else
             
               icsp_start  <= '1';
               burst_count <= burst_count - 1;  
               state  <= s_wait_icsp_done;
             
             end if;
               
           end if;
         
         end if;
        
       -- ----------------------------------------------------------------------
       -- SEND_ACK
       -- ----------------------------------------------------------------------
       when s_send_ack=>
       
         uart_wdata <= reg_data(7 downto 0);
         uart_wen   <= '1';
         state <= s_wait_sending_ack;
        
       when s_wait_sending_ack=>
       
         if uart_busy_1t='1' and uart_busy='0' then
         
           state <= s_idle;
         
         end if;
        
      end case;
    
    end if;
  
  end process p_fsm;

  -- ---------------------------------------------------------------------------
  -- ICSP instance
  -- ---------------------------------------------------------------------------
  icsp_0:icsp
  port map
  (
    -- system
    resetn        => root50m_nrst,
    clk           => osc50,
    
    -- control 
    start         => icsp_start,
    done          => icsp_done,
    command       => icsp_command,
    datai         => icsp_datai,
    datao         => icsp_datao,
     
    -- delays
    delay_hperiod => reg_delay_hperiod,
    delay_prog    => reg_delay_prog,
    delay_erase   => reg_delay_erase,
    
    -- PIC pins
    pgc           => icsp_pgc,
    pgdo          => icsp_pgdo,
    pgdi          => icsp_pgdi,
    pgde          => icsp_pgde
  );

  -- ---------------------------------------------------------------------------
  -- UART instance
  -- ---------------------------------------------------------------------------
  uart_0:uart
  port map
  (
    -- system
    resetn   => root50m_nrst,
    clk      => osc50,
    clken    => uart_clken,
    
    -- tx
    wen      => uart_wen,
    data_in  => uart_wdata,
    busy     => uart_busy,
    tx       => uart_tx,
    
    -- rx
    rx       => uart_rx,
    data_out => uart_rdata,
    valid    => uart_valid
  );

end architecture rtl;
